#ifndef _AST_H
#define _AST_H
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
#include <algorithm>
#include <cassert>
#include <cctype>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include <iostream>
using namespace llvm;
using namespace llvm::sys;
/// ExprAST - Base class for all expression nodes.
class ExprAST
{
public:
  virtual ~ExprAST() = default;

  virtual Value *codegen() = 0;
};

/// NumberExprAST - Expression class for numeric literals like "1.0".
class NumberDoubleExprAST : public ExprAST
{
  double Val;

public:
  NumberDoubleExprAST(double Val) : Val(Val) {}

  Value *codegen() override;
};

class NumberIntExprAST : public ExprAST
{
  int Val;

public:
  NumberIntExprAST(int Val) : Val(Val) {}

  Value *codegen() override;
};
class NumberBoolExprAST : public ExprAST
{
  bool Val;

public:
  NumberBoolExprAST(bool Val) : Val(Val) {}

  Value *codegen() override;
};
class NumberCharExprAST : public ExprAST
{
  char Val;

public:
  NumberCharExprAST(char Val) : Val(Val) {}

  Value *codegen() override;
};
class NumberStringExprAST : public ExprAST
{
  std::string Val;

public:
  NumberStringExprAST(std::string Val) : Val(Val) {}

  Value *codegen() override;
};
/// VariableExprAST - Expression class for referencing a variable, like "a".
class VariableExprAST : public ExprAST
{
  std::string Name;

public:
  VariableExprAST(const std::string &Name) : Name(Name) {}

  bool isType;
  bool isArray;

  Value *codegen() override;
};

/// BinaryExprAST - Expression class for a binary operator.
class BinaryExprAST : public ExprAST
{
  int Op;
  std::unique_ptr<ExprAST> LHS, RHS;

public:
  BinaryExprAST(int Op, std::unique_ptr<ExprAST> LHS,
                std::unique_ptr<ExprAST> RHS)
      : Op(Op), LHS(std::move(LHS)), RHS(std::move(RHS)) {}

  Value *codegen() override;
};

/// CallExprAST - Expression class for function calls.
class CallExprAST : public ExprAST
{
  std::string Callee;
  std::vector<std::unique_ptr<ExprAST>> Args;

public:
  CallExprAST(const std::string &Callee,
              std::vector<std::unique_ptr<ExprAST>> Args)
      : Callee(Callee), Args(std::move(Args)) {}

  Value *codegen() override;
};

class StatementAST
{
public:
  virtual ~StatementAST() = default;
  virtual bool codegen() = 0;
};
class DeclAST : public StatementAST
{
  std::string type;
  std::vector<std::string> variables;

public:
  DeclAST(std::string type, std::vector<std::string> variables) : type(type), variables(std::move(variables))
  {
  }
  bool codegen() override;
};
class SimpAST : public StatementAST
{
  std::string Name;
  std::unique_ptr<ExprAST> expression;

public:
  SimpAST(const std::string &name, std::unique_ptr<ExprAST> expr) : Name(name), expression(std::move(expr))
  {
  }
  bool codegen() override;
};
class DeclSimpAST : public StatementAST
{
  std::string type;
  std::string Name;
  std::unique_ptr<ExprAST> expression;

public:
  DeclSimpAST(std::string type, const std::string &name, std::unique_ptr<ExprAST> expr) : type(type), Name(name), expression(std::move(expr))
  {
  }
  bool codegen() override;
};
class ReturnAST : public StatementAST
{
  std::unique_ptr<ExprAST> expression;

public:
  ReturnAST(std::unique_ptr<ExprAST> expr) : expression(std::move(expr))
  {
  }
  bool codegen() override;
};
class NonAssignAST : public StatementAST
{
  std::unique_ptr<ExprAST> expression;

public:
  NonAssignAST(std::unique_ptr<ExprAST> expr) : expression(std::move(expr))
  {
  }
  bool codegen() override;
};
class BodyAST
{
  std::vector<std::unique_ptr<StatementAST>> stmts;

public:
  void add(std::unique_ptr<StatementAST> stmt) { stmts.push_back(std::move(stmt)); };
  bool codegen();
};
class IFAST : public StatementAST
{
  std::unique_ptr<ExprAST> condExpr;
  std::unique_ptr<BodyAST> trueBlock;
  std::unique_ptr<BodyAST> falseBlock;

public:
  IFAST(std::unique_ptr<ExprAST> condExpr, std::unique_ptr<BodyAST> trueBlock, std::unique_ptr<BodyAST> falseBlock) : condExpr(std::move(condExpr)), trueBlock(std::move(trueBlock)), falseBlock(std::move(falseBlock))
  {
  }
  bool codegen() override;
};
class WhileAST : public StatementAST
{
private:
  std::unique_ptr<ExprAST> condExpr;
  std::unique_ptr<BodyAST> block;

public:
  WhileAST(std::unique_ptr<ExprAST> condExpr, std::unique_ptr<BodyAST> block)
      : condExpr(std::move(condExpr)), block(std::move(block))
  {
  }
  bool codegen() override;
};
class ForAST : public StatementAST
{
private:
  std::unique_ptr<StatementAST> init;
  std::unique_ptr<ExprAST> condExpr;
  std::unique_ptr<BodyAST> block;
  std::unique_ptr<StatementAST> after;

public:
  ForAST(std::unique_ptr<StatementAST> init, std::unique_ptr<ExprAST> condExpr, std::unique_ptr<BodyAST> block, std::unique_ptr<StatementAST> after)
      : init(std::move(init)), condExpr(std::move(condExpr)), block(std::move(block)), after(std::move(after))
  {
  }
  bool codegen() override;
};
/// PrototypeAST - This class represents the "prototype" for a function,
/// which captures its name, and its argument names (thus implicitly the number
/// of arguments the function takes).
class PrototypeAST
{
  std::string Name;
  std::vector<std::string> Args;
  std::vector<std::string> ArgTypes;
  std::map<std::string, std::string> typeMap;
  std::string FnType;

public:
  PrototypeAST(const std::string &Name, std::vector<std::string> Args, std::vector<std::string> ArgTypes, std::string FnType)
      : Name(Name), Args(std::move(Args)), ArgTypes(std::move(ArgTypes)), FnType(FnType)
  {
    for (int i = 0; i < this->Args.size(); i++)
    {
      std::string name = this->Args[i];
      typeMap[name] = this->ArgTypes[i];
    }
  }

  Function *codegen();
  const std::string &getName() const { return Name; }
  const std::string getReturnType() { return FnType; }
  const std::vector<std::string> &getArgTypes() { return ArgTypes; }
  std::string getArgType(std::string name)
  {
    if (typeMap.find(name) != typeMap.end())
    {
      return typeMap[name];
    }

    return 0;
  }
};

/// FunctionAST - This class represents a function definition itself.
class FunctionAST
{
  std::unique_ptr<PrototypeAST> Proto;
  std::unique_ptr<BodyAST> Body;

public:
  FunctionAST(std::unique_ptr<PrototypeAST> Proto,
              std::unique_ptr<BodyAST> Body)
      : Proto(std::move(Proto)), Body(std::move(Body)) {}

  Function *codegen();
};
class StructAST{
  std::string name;
  std::vector<std::string>members;
  std::vector<std::string>memberTypes;
  public:
    StructAST(std::string name,std::vector<std::string>members,std::vector<std::string>memberTypes)
      :name(name),members(std::move(members)),memberTypes(std::move(memberTypes)){

    }
    std::string getName(){return name;}
    void codegen();
};
class StructMemberReadAST : public ExprAST
{
private:
  std::string varName;
  std::string memberName;
public:
  StructMemberReadAST(std::string varName,std::string memberName)
    :varName(varName),memberName(memberName){

    };
  Value *codegen() override;
};
class StructMemberWriteAST : public StatementAST
{
private:
  std::string varName;
  std::string memberName;
  std::unique_ptr<ExprAST> expr;
public:
  StructMemberWriteAST(std::string varName,std::string memberName,std::unique_ptr<ExprAST> expr)
    :varName(varName),memberName(memberName),expr(std::move(expr)){

    };
  bool codegen() override;
};

std::unique_ptr<ExprAST> LogError(const char *Str);
std::unique_ptr<PrototypeAST> LogErrorP(const char *Str);
std::unique_ptr<FunctionAST> LogErrorF(const char *Str);
std::unique_ptr<StatementAST> LogErrorS(const char *Str);
Value *LogErrorV(const char *Str);
bool LogErrorB(const char *Str);
std::unique_ptr<StructAST> LogErrorStruct(const char *Str);
#endif